﻿using EWSEditor.Common;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EWSEditor.CRM.Forms
{
    public partial class CrmSyncEmulatorForm : Form
    {

        private Dictionary<string, EntityCollection> _filterCollection = new Dictionary<string, EntityCollection>();
        private OrganizationServiceProxy _orgProxy;
        private ListViewColumnSorter _lvwColumnSorterRaw;
        private ListViewColumnSorter _lvwColumnSorterActual;
        private ListViewColumnSorter _lvwColumnSorterOmitted;
        private ListViewColumnSorter _lvwColumnSorterUnchanged;

        public CrmSyncEmulatorForm()
        {
            InitializeComponent();
            _lvwColumnSorterRaw = new ListViewColumnSorter();
            _lvwColumnSorterActual = new ListViewColumnSorter();
            _lvwColumnSorterOmitted = new ListViewColumnSorter();
            _lvwColumnSorterUnchanged = new ListViewColumnSorter();
            _orgProxy = CrmAuthInfoFactory.GetOrgProxy();
            CrmOrgMailboxCache.RetrieveAndSetTargetMailbox(_orgProxy);
            Thread RetrieveFiltersThread = new Thread(new ThreadStart(RetrieveFilters));
            RetrieveFiltersThread.SetApartmentState(ApartmentState.STA);
            RetrieveFiltersThread.Start();
        }

        private void RetrieveFilters()
        {
            QueryByAttribute query = new QueryByAttribute("userquery");
            query.ColumnSet = new ColumnSet( new string[] { "fetchxml", "name", "returnedtypecode" });
            query.Attributes.AddRange("querytype");
            query.Values.AddRange(256);
            query.Attributes.AddRange("ownerid");
            query.Values.AddRange(CrmOrgMailboxCache.LoggedInSystemUser.Id);
            query.Attributes.AddRange("statecode");
            query.Values.AddRange(0);

            EntityCollection filters = _orgProxy.RetrieveMultiple(query);

            if (filters.Entities.Count > 0)
            {
                PopulateFilterCollection(filters);
                SetPicklistOptions();
            }
            else
            {
                this.Invoke(new Action(ShowError));
            }

        }

        private void SetPicklistOptions()
        {
            this.Invoke(new Action(
                () =>
                
                {
                    foreach (string key in _filterCollection.Keys)
                    {
                        cmbEntity.Items.Add(key);
                    }
                }

                )
                );
        }

        private void ShowError()
        {
            MessageBox.Show("No synchronization filters found.");
        }

        private void PopulateFilterCollection(EntityCollection filters)
        {
            foreach (Entity filter in filters.Entities)
            {
                string typecode = (string)filter["returnedtypecode"];
                if (!_filterCollection.ContainsKey(typecode))
                {
                    _filterCollection.Add(typecode, new EntityCollection());
                    _filterCollection[typecode].Entities.Add(filter);
                }
                else
                {
                    _filterCollection[typecode].Entities.Add(filter);
                }
            }
        }

        private void cmbEntity_SelectedIndexChanged(object sender, EventArgs e)
        {
            EntityCollection filters = _filterCollection[cmbEntity.SelectedItem.ToString()];
            List<SyncEntity> syncEntities = new List<SyncEntity>();

            foreach (Entity filter in filters.Entities)
            {
                SyncEntity syncFilterEntity = new SyncEntity(filter);
                syncFilterEntity.Name = (string)filter["name"];
                syncEntities.Add(syncFilterEntity);
            }

            BindingSource boundsrc = new BindingSource();
            boundsrc.DataSource = syncEntities;
            cmbFilter.DataSource = boundsrc.DataSource;
            cmbFilter.DisplayMember = "Name";
            cmbFilter.ValueMember = "Name";

        }

        private void cmbFilter_SelectedIndexChanged(object sender, EventArgs e)
        {
            txtFetchXml.Text = (string)((SyncEntity)cmbFilter.SelectedItem)["fetchxml"];
        }

        private void btnTestSynchronization_Click(object sender, EventArgs e)
        {
            if ((cmbFilter.SelectedItem == null) || (cmbEntity.SelectedItem == null))
            {
                MessageBox.Show(this, "Please select an entity.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            Cursor = Cursors.WaitCursor;
            ClearGridsAndSorting();
            string entityName = (string)((SyncEntity)cmbFilter.SelectedItem)["returnedtypecode"];
            string fetchXml = (string)((SyncEntity)cmbFilter.SelectedItem)["fetchxml"];
            CrmSynchronizationContext syncContext = new CrmSynchronizationContext(entityName, (SyncEntity)cmbFilter.SelectedItem, CrmOrgMailboxCache.SubscriptionId);
            syncContext.Initialize(chkRunAll.Checked, _filterCollection[entityName]);
            PopulateLastSyncStartedTime(syncContext);
            PopulateTabGrids(entityName, syncContext);
            Cursor = Cursors.Default;
        }

        private void PopulateLastSyncStartedTime(CrmSynchronizationContext syncContext)
        {
            StringBuilder sb = new StringBuilder("Last Server Side Sync Event Started On ");
            sb.Append(syncContext.LastSyncStartedOn);
            tslblLastSyncTime.Text = sb.ToString();
        }

        private void ClearGridsAndSorting()
        {
            listRawResults.Items.Clear();
            listActualResults.Items.Clear();
            listOmittedResults.Items.Clear();
            listUnchangedItems.Items.Clear();
            listActualResults.ListViewItemSorter = null;
            listOmittedResults.ListViewItemSorter = null;
            listRawResults.ListViewItemSorter = null;
            listUnchangedItems.ListViewItemSorter = null;
        }

        private void PopulateTabGrids(string entityName, CrmSynchronizationContext syncContext)
        {
            int otc = SharedUtilities.GetTypeCodeFromActType(entityName);

            PopulateSingleGrid(otc, syncContext.Results, listRawResults, lblRawCount);
            PopulateSingleGrid(otc, syncContext.OmittedResults, listOmittedResults, lblOmittedCount);
            PopulateSingleGrid(otc, syncContext.ActualResults, listActualResults, lblActualCount);
            PopulateSingleGrid(otc, syncContext.UnchangedItems, listUnchangedItems, lblUnchangedCount);

            listActualResults.ListViewItemSorter = _lvwColumnSorterActual;
            listOmittedResults.ListViewItemSorter = _lvwColumnSorterOmitted;
            listRawResults.ListViewItemSorter = _lvwColumnSorterRaw;
            listUnchangedItems.ListViewItemSorter = _lvwColumnSorterUnchanged;

        }

        private void PopulateSingleGrid(int otc, List<SyncEntity> items, ListView list, System.Windows.Forms.Label lblCount)
        {
            foreach (SyncEntity context in items)
            {
                ListViewItem item = new ListViewItem();
                item.Tag = context;

                if ((otc == 4201) || (otc == 4212) || (otc == 4204) || (otc == 4207) || (otc == 4210) || (otc == 4214) || (otc == 4251))
                {
                    item.Text = (string)context["subject"];
                }
                else
                {
                    item.Text = (string)context["fullname"];
                }
                item.SubItems.Add(((DateTime)context["modifiedon"]).ToString());
                item.SubItems.Add(((DateTime)context["createdon"]).ToString());
                list.Items.Add(item);
            }

            lblCount.Text = new StringBuilder("Total Items: ").Append(list.Items.Count).ToString();
        }

        private void btnOpenInDynamics_Click(object sender, EventArgs e)
        {
            string currentTab = tabctResults.SelectedTab.Name;
            ListView currentListView;

            switch (currentTab)
            {
                case "tabRawResults":
                    currentListView = listRawResults;
                    OpenInDynamics(currentListView);
                    break;
                case "tabOmittedItems":
                    currentListView = listOmittedResults;
                    OpenInDynamics(currentListView);
                    break;
                case "tabActualResults":
                    currentListView = listActualResults;
                    OpenInDynamics(currentListView);
                    break;

            }
        }

        private void OpenInDynamics(ListView currentListView)
        {
            if (currentListView.SelectedItems.Count <= 0)
            {
                return;
            }

            SyncEntity selectedItem = (SyncEntity)currentListView.SelectedItems[0].Tag;
            int typeCode = SharedUtilities.GetTypeCodeFromActType(selectedItem.LogicalName);
            SharedUtilities.OpenInCRM(typeCode, selectedItem.Id.ToString());
        }

        private void CrmSyncEmulatorForm_Load(object sender, EventArgs e)
        {
            if (((OptionSetValue)CrmOrgMailboxCache.TargetMailbox["actstatus"]).Value != 1)
            {
                btnOpenInDynamics.Enabled = false;
                btnTestSynchronization.Enabled = false;
                MessageBox.Show(Strings.NONACTENABLEDMAILBOX, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            StringBuilder sb = new StringBuilder("Last Server Side Sync Event Started On ");
            sb.Append(CrmSynchronizationContext.GetLastSyncStartTime());
            tslblLastSyncTime.Text = sb.ToString();
        }

        private void listRawResults_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            HandleColumnSort((ListView)sender, e, _lvwColumnSorterRaw);
        }

        private void listActualResults_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            HandleColumnSort((ListView)sender, e, _lvwColumnSorterActual);
        }

        private void listUnchangedItems_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            HandleColumnSort((ListView)sender, e, _lvwColumnSorterUnchanged);
        }

        private void listOmittedResults_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            HandleColumnSort((ListView)sender, e, _lvwColumnSorterOmitted);
        }

        private void HandleColumnSort(ListView listView, ColumnClickEventArgs e, ListViewColumnSorter sorter)
        {
            // Determine if clicked column is already the column that is being sorted.
            if (e.Column == sorter.SortColumn)
            {
                // Reverse the current sort direction for this column.
                if (sorter.Order == SortOrder.Ascending)
                {
                    sorter.Order = SortOrder.Descending;
                }
                else
                {
                    sorter.Order = SortOrder.Ascending;
                }
            }
            else
            {
                // Set the column number that is to be sorted; default to ascending.
                sorter.SortColumn = e.Column;
                sorter.Order = SortOrder.Ascending;
            }

            // Perform the sort with these new sort options.
            listView.Sort();
        }
    }
}